﻿/*
Copyright (c) 2010, hiSoft
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the hiSoft nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE HISOFT AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL HISOFT BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.IO;
using System.Collections.Generic;

namespace Orchard.Test.Library
{   
    public class UserHelper : UIHelperBase
    {
        public UserHelper(TestLibrary settings) : base(settings) { }

        public void LogOnAsAdmin()
        {
            LogOn(TestLibrary.Consts.AdminUserName, TestLibrary.Consts.CommonPassword);
        }

        public void LogOnAsDefaultUser()
        {
            LogOn(TestLibrary.Consts.DefaultUserName, TestLibrary.Consts.CommonPassword);
        }

        public void LogOn(string username, string password)
        {
            //Because install a new module into "Orchard.Web\Modules" will impact full testing
            //And sometime add new module will cause that exception/error occurred during the compilation of orchard
            //So add this try catch block so that delete the temp new module in advance, then try to logon orchard again
            try
            {
                selenium.Open("/Users/Account/LogOn");


                if (selenium.IsElementPresent("link=Sign Out"))
                {
                    ClickAndWait("link=Sign Out");
                    selenium.Open("/Users/Account/LogOn");
                }

                selenium.Type("username-email", username);
                selenium.Type("password", password);
            }
            catch (Exception ex)
            {
                string message = ex.Message;
                string findStrBegin = "Source File:";
                string findStrEnd = "Line:";
                int start = 0;
                int end = 0;
                int length = 0;
                string path = "";
                string moduleFileName = "";
                string htmlSource = selenium.GetHtmlSource();
                string actualPathofNeedDeleteModule = "";
                if (htmlSource.Contains(findStrBegin))
                {
                    start = htmlSource.IndexOf(findStrBegin) + findStrBegin.Length;
                    end = htmlSource.IndexOf(findStrEnd);
                    length = end - start;
                    if (length > 1)
                        path = htmlSource.Substring(start, length);
                    moduleFileName = path.Substring(path.IndexOf("Modules") + 8);  //8 equal to legth of string "modules" and "\"
                    moduleFileName = moduleFileName.Split('\\')[0];
                    actualPathofNeedDeleteModule = TestLibrary.Consts.OrchardModulesPath + "\\" + moduleFileName;
                    if (Directory.Exists(actualPathofNeedDeleteModule))
                    {
                        for (int i = 0; i < 3; i++)
                        {
                            try
                            {
                                Directory.Delete(actualPathofNeedDeleteModule, true);
                            }
                            catch { }                                 
                            if (!Directory.Exists(actualPathofNeedDeleteModule))
                                break;
                        }
                    }
                }

            }
            finally
            {
                selenium.Open("/Users/Account/LogOn");

                if (selenium.IsElementPresent("link=Sign Out"))
                {
                    ClickAndWait("link=Sign Out");
                    selenium.Open("/Users/Account/LogOn");
                }

                selenium.Type("username-email", username);
                selenium.Type("password", password);
            }

            Submit();           
        }

        public void LogOff()
        {
            selenium.Open("/Users/Account/LogOff");
        }   
     
        public int GetUserIndexOnManageUsersPage(User user)
        {
          return GetUserIndexOnManageUsersPage(user.UserName);
        }

        public int GetUserIndexOnManageUsersPage(string userName)
        {
            ManageUsers();
            var count = (int)selenium.GetXpathCount(UsersLocator);
            var index = 1;
            for (; index <= count; ++index)
            {
                string path = String.Format("{0}[{1}]/td[2]/a", UsersLocator, index);
                var text = selenium.GetText(path);
                if (String.CompareOrdinal(userName, text) == 0)
                    break;
            }

            if (index > count)
                throw new CaseFailureException(String.Format("Can't find user {0} in Manage Users page!", userName));

            return index;
        }

        private int GetActionIndexByText(int userIndex, string actionText)
        {
            var actionCount = (int)selenium.GetXpathCount(String.Format("{0}[{1}]/td[4]/a", UsersLocator, userIndex));
            for (int i = 1; i <= actionCount; i++)
            {
                if (selenium.GetText(String.Format("{0}[{1}]/td[4]/a[{2}]", UsersLocator, userIndex, i)) == actionText)
                {
                    return i;
                }
            }
            return 0;
        }  

        public void ManageUsers()
        {
            if (!selenium.GetLocation().EndsWith("/Admin/Users"))
            {
                selenium.Open("/Admin/Users");
            }
        }

        public bool IsUsernamePresent(User user)
        {
            ManageUsers();
            return selenium.IsElementPresent("link=" + user.UserName);
        }

        public bool IsUserEmailPresent(User user)
        {
            ManageUsers();
            return selenium.IsTextPresent(user.Email);
        }

        public int GetUserCountOnManageUsers()
        {
            ManageUsers();
            string usersCountStr = selenium.GetText("//span[@class='page-results']");
            string[] usersCountStrs = usersCountStr.Split(' ');
            return Int32.Parse(usersCountStrs[usersCountStrs.Length - 1]);
        }

        public User AddRandomUser()
        {
            return AddUser(new UserSettings());
        }       

        public User AddUser(UserSettings userSetting)
        {
            selenium.Open("/Admin/Users/Create");
            selenium.Type("UserName", userSetting.UserName);
            selenium.Type("Email", userSetting.Email);
            selenium.Type("Password", userSetting.Password);
            selenium.Type("ConfirmPassword", userSetting.ConfirmPassword);            
            if (userSetting.Roles != null && userSetting.Roles.Count != 0)
            {
                foreach (Role role in userSetting.Roles)
                {
                    int index = role.GetRoleIndexOnAddUserPage();
                    selenium.Check(String.Format("{0}[{1}]/input[1]", RolesLocatorForAddUser, index));
                }
            }
            Submit();
            return new User(userSetting);
        }

        public User RegisterUser(UserSettings userSetting)
        {
            selenium.Open("/Users/Account/Register");
            selenium.Type("username", userSetting.UserName);
            selenium.Type("email", userSetting.Email);
            selenium.Type("password", userSetting.Password);
            selenium.Type("confirmPassword", userSetting.ConfirmPassword);
            Submit();
            return new User(userSetting);
        }

        public User RegisterUserInSpecial(UserSettings userSetting)
        {            
            selenium.Type("username", userSetting.UserName);
            selenium.Type("email", userSetting.Email);
            selenium.Type("password", userSetting.Password);
            selenium.Type("confirmPassword", userSetting.ConfirmPassword);
            Submit();
            return new User(userSetting);
        }

        public User EditUser(User user, UserSettings userSetting)
        {
            if (user != null)
            {
                DoActionToUser(user, UserAction.Edit);
                selenium.Type("UserName", userSetting.UserName);
                selenium.Type("Email", userSetting.Email);
                if (userSetting.Roles != null && userSetting.Roles.Count != 0)
                {
                    int roleCnt = (int)selenium.GetXpathCount(RolesLocatorForEditUser);
                    for (int i = 1; i <= roleCnt; i++)
                    {

                        if (selenium.IsChecked(String.Format("{0}[{1}]/input[1]", RolesLocatorForEditUser, i)))
                            selenium.Uncheck(String.Format("{0}[{1}]/input[1]", RolesLocatorForEditUser, i));
                    }
                    foreach (Role role in userSetting.Roles)
                    {
                        int index = role.GetRoleIndexOnEditUserPage();
                        selenium.Check(String.Format("{0}[{1}]/input[1]", RolesLocatorForEditUser, index));
                    }
                }
                else
                {
                    int roleCnt = (int)selenium.GetXpathCount(RolesLocatorForEditUser);
                    for (int i = 1; i <= roleCnt; i++)
                    {

                        if (selenium.IsChecked(String.Format("{0}[{1}]/input[1]", RolesLocatorForEditUser, i)))
                            selenium.Uncheck(String.Format("{0}[{1}]/input[1]", RolesLocatorForEditUser, i));
                    }
                }
          
                Submit();
            }
            return new User(userSetting);
        }      

        public void DoActionToUser(User user, UserAction action)
        {
            DoActionToUser(user.UserName, action);
        }

        public void DoActionToUser(string userName, UserAction action)
        {
            if (!string.IsNullOrEmpty(userName))
            {
                int userIndex = GetUserIndexOnManageUsersPage(userName);
                string actionText = null;
                switch (action)
                {
                    case UserAction.Approve:
                        actionText = "Approve";
                        break;
                    case UserAction.ChallengeEmail:
                        actionText = "Send challenge E-mail";
                        break;
                    case UserAction.Disable:
                        actionText = "Disable";
                        break;
                    case UserAction.Edit:
                        actionText = "Edit";
                        break;
                    case UserAction.Delete:
                        actionText = "Delete";
                        break;
                }
                int actionIndex = GetActionIndexByText(userIndex, actionText);
                ClickAndWait(String.Format("{0}[{1}]/td[4]/a[{2}]", UsersLocator, userIndex, actionIndex));
            }
        }

        private void CheckUser(User user)
        {
            CheckUser(user.UserName);
        }

        private void CheckUser(string userName)
        {
            int userIndex = GetUserIndexOnManageUsersPage(userName);
            selenium.Check(String.Format("//input[@name='Users[{0}].IsChecked']", userIndex - 1));
        }

        public void DoSelectActionToUser(User user, UserAction action)
        {
            DoSelectActionToUser(user.UserName, action);
        }

        public void DoSelectActionToUser(string userName, UserAction action)
        {
            CheckUser(userName);
            switch (action)
            {
                case UserAction.NoAction:
                    selenium.Select("publishActions", "label=Choose action...");
                    break;
                case UserAction.Approve:
                    selenium.Select("publishActions", "label=Approve");
                    break;
                case UserAction.Disable:
                    selenium.Select("publishActions", "label=Disable");
                    break;
                case UserAction.ChallengeEmail:
                    selenium.Select("publishActions", "label=Send challenge E-mail");
                    break;
                case UserAction.Delete:
                    selenium.Select("publishActions", "label=Delete");
                    break;
            }
            ClickAndWait("//button[@name='submit.BulkEdit']");
        }

        public void FilterUsersByKeyword(string keyword)
        {
            ManageUsers();
            selenium.Type("Options_Search", keyword);
            ClickAndWait("//button[@name='submit.Filter']");
        }

        public void FilterUsersByUserStatus(UserStatusFilter statusFilter)
        {
            ManageUsers();
            switch (statusFilter)
            {
                case UserStatusFilter.AllUsers:
                    selenium.Select("//select[@name='Options.Filter']", "label=All Users");
                    break;
                case UserStatusFilter.ApprovedUsers:
                    selenium.Select("//select[@name='Options.Filter']", "label=Approved Users");
                    break;
                case UserStatusFilter.PendingUsers:
                    selenium.Select("//select[@name='Options.Filter']", "label=Pending Users");
                    break;
                case UserStatusFilter.PendingEmails:
                    selenium.Select("//select[@name='Options.Filter']", "label=Pending Emails");
                    break;
            }
            ClickAndWait("//button[@name='submit.Filter']");
        }

        public void SortUsersBy(UserSortCondition sortCondition)
        {
            ManageUsers();
            switch (sortCondition)
            {
                case UserSortCondition.Name:
                    selenium.Select("//select[@name='Options.Order']", "label=Name");
                    break;
                case UserSortCondition.Email:
                    selenium.Select("//select[@name='Options.Order']", "label=Email");
                    break;
            }
            ClickAndWait("//button[@name='submit.Filter']");
        }

        public string GetModerateActionText(User user)
        {
            if (user != null)
            {
                int userIndex = GetUserIndexOnManageUsersPage(user);
                return selenium.GetText(String.Format("{0}[{1}]/td[4]/a[3]", UsersLocator, userIndex));
            }
            return null; 
        }

        public void RequestLostPassword(User user)
        {
            selenium.Open("/Users/Account/RequestLostPassword");
            selenium.Type("username", user.Email);
            Submit();
        }

        public User ChangePassword(User user, string currentPassword, string newPassword, string confirmPassword)
        {
            InputUserInfo_ChangePassword(currentPassword, newPassword, confirmPassword);
            var newUser = new User(new UserSettings() { UserName = user.UserName, Email = user.Email, Password = newPassword });            
            return newUser;
        }

        public void InputUserInfo_ChangePassword(string currentPassword, string newPassword, string confirmPassword)
        {
            selenium.Open("/Users/Account/ChangePassword");
            selenium.Type("currentPassword", currentPassword);
            selenium.Type("newPassword", newPassword);
            selenium.Type("confirmPassword", confirmPassword);
            Submit();
        }

        /// <summary>
        /// Use 163.com mailBox as test sender email(has been registered before),As default, open the first mail of Inbox
        /// </summary>
        /// <param name="username">username to logon mailbox</param>
        /// <param name="password">password for this 163 mailbox</param>
        public void LogOnTest163MailAndOpenInbox(string username, string password)
        {
            selenium.SetTimeout("90000");
            try
            {
                selenium.Open("http://mail.163.com/");
            }
            catch (Exception)
            { } //don't care this exception if exist.
            selenium.Type("name=username", username);
            selenium.Type("name=password", password);
            selenium.Click("//button[@type='submit']");
            selenium.WaitForPageToLoad("30000");
            System.Threading.Thread.Sleep(3000);
            selenium.Click("id=_js4_link_0_2");  //selenium.Click("link=收件箱");  locator of Inbox of 163 Mail
            System.Threading.Thread.Sleep(3000);
            try
            {
                if (!selenium.IsElementPresent("//li[1]/div[2]"))
                    selenium.Click("id=_js4_link_0_2");
            }
            catch (Exception)
            { }
            System.Threading.Thread.Sleep(3000);
            selenium.Click("//li[1]/div[2]"); //the first mail as default
            System.Threading.Thread.Sleep(5000);
        }

        public bool IsClickLinkInTest163InboxWhenRequestLostPwdOk()
        {
            if(!selenium.IsElementPresent("link=click here"))
            {
                return false;
            }            
            string link = selenium.GetAttribute("link=click here@href");
            selenium.Open(link);
            System.Threading.Thread.Sleep(6000);
            return true;
        }

        //ChallengeEmail
        public bool IsValidateRegisterLinkFrom163MailOk()
        {
            string strLocatorOfValidateLink = "//a[contains(text(),'http://localhost/Users/Account/ChallengeEmail?nonce')]";
            if (!selenium.IsElementPresent(strLocatorOfValidateLink))
            {
                return false;
            }
            selenium.Click(strLocatorOfValidateLink);           
            System.Threading.Thread.Sleep(4000);            
            return true;
        }

    }

    public class User
    {
        public UserSettings UserSetting { get; private set; }
        public string UserName { get; private set; }
        public string Email { get; private set; }
        public string Password { get; private set; }
        public List<Role> Roles { get; set; }

        public User(UserSettings userSetting)
        {
            UserName = userSetting.UserName;
            Email = userSetting.Email;
            Password = userSetting.Password;
            if (userSetting.Roles != null && userSetting.Roles.Count != 0)
            {
                Roles = new List<Role>();
                foreach (Role role in userSetting.Roles)
                {
                    Roles.Add(role);
                }
            }
        }
    }

    public class UserSettings
    {
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public string ConfirmPassword { get; set; }
        public List<Role> Roles { get; set; }

        public UserSettings()
        {
            string uniqueIdentifier = UIHelperBase.GenerateUniqueIdentifier(DateTime.Now);
            UserName = String.Format("User{0}", uniqueIdentifier);
            Email = String.Format("{0}@microsoft.com", uniqueIdentifier);
            Password = TestLibrary.Consts.CommonPassword;
            ConfirmPassword = Password;
            Roles = null;
        }
    }

    public enum UserAction
    {
        Edit,
        Delete,
        Disable,
        Approve,
        ChallengeEmail,
        NoAction
    }

    public enum UserStatusFilter
    {
        AllUsers,
        ApprovedUsers,
        PendingUsers,
        PendingEmails
    }

    public enum UserSortCondition
    {
        Name,
        Email
    }
}
